.

iT邦幫忙

2022 iThome 鐵人賽

DAY 25
1
AI & Data

Python x AI 影像辨識好好玩系列 第 25

( Day 25 ) Mediapipe 辨識手指,用手指擦除霧氣

  • 分享至 

  • xImage
  •  

這篇教學會延伸「Mediapipe 手掌辨識」和「在影片中即時繪圖」文章,並應用「OpenCV 影像遮罩」功能,實作一個「用手指擦除鏡子霧氣」的趣味效果 ( 食指和中指分開時不會擦除,食指中指併攏就會擦除 )。

原文參考:Mediapipe 辨識手指,用手指擦除霧氣

因為程式使用 Jupyter 搭配 Tensorflow 進行開發,所以請先閱讀「使用 Anaconda」和「使用 MediaPipe」,安裝對應的套件,如果不要使用 Juputer,也可參考「使用 Python 虛擬環境」,建立虛擬環境進行實作。

Python 教學 - Mediapipe 辨識手指,做出手指擦除霧氣的效果

使用遮罩,結合模糊與清楚的影像

參考「影像遮罩」文章,實作「清楚影像裡面,有一個模糊區域」的效果,過程的原理如下:

  • 使用 NumPy 產生兩個遮罩,一個遮罩給「清楚的影像」,一個遮罩給「模糊的影像」。
  • 清楚影像的遮罩,需要套用模糊的部分為黑色,其他為白色
  • 模糊影像的遮罩,需要套用模糊的部分為白色,其他為黑色
  • 將遮罩轉換為灰階後,使用 cv2.bitwise_and 方法套用遮罩。
  • 遮罩套用完成,使用 cv2.add 方法合併影像。

Python 教學 - Mediapipe 辨識手指,做出手指擦除霧氣的效果

下方的程式碼執行後,會在攝影機的影像中,即時套用遮罩的效果。

import cv2
import numpy as np

w = 640    # 定義影片寬度
h = 360    # 定義影像高度
dots = []  # 記錄座標
mask_b = np.zeros((h,w,3), dtype='uint8')   # 產生黑色遮罩 -> 套用清楚影像
mask_b[:, :] = 255                          # 設定黑色遮罩底色為白色
mask_b[80:280, 220:420] = 0                 # 設定黑色遮罩哪個區域是黑色

mask_w = np.zeros((h,w,3), dtype='uint8')   # 產生白色遮罩 -> 套用模糊影像
mask_w[80:280, 220:420] = 255               # 設定白色遮罩哪個區域是白色

cap = cv2.VideoCapture(0)

if not cap.isOpened():
    print("Cannot open camera")
    exit()
while True:
    ret, img = cap.read()
    if not ret:
        print("Cannot receive frame")
        break

    img = cv2.resize(img,(w,h))                      # 縮小尺寸,加快速度
    img = cv2.flip(img, 1)                           # 翻轉影像
    img = cv2.cvtColor(img, cv2.COLOR_BGR2BGRA)      # 轉換顏色為 BGRA ( 計算時需要用到 Alpha 色版 )
    img2 = img.copy()                                # 複製影像
    img2 = cv2.blur(img, (55, 55))                   # 套用模糊

    mask1 = cv2.cvtColor(mask_b, cv2.COLOR_BGR2GRAY) # 轉換遮罩為灰階
    img = cv2.bitwise_and(img, img, mask=mask1)      # 清楚影像套用黑遮罩

    mask2 = cv2.cvtColor(mask_w, cv2.COLOR_BGR2GRAY) # 轉換遮罩為灰階
    img2 = cv2.bitwise_and(img2, img2, mask=mask2)   # 模糊影像套用白遮罩

    output = cv2.add(img, img2)                      # 合併影像

    cv2.imshow('oxxostudio', output)
    if cv2.waitKey(50) == ord('q'):
        break

cap.release()
cv2.destroyAllWindows()

Python 教學 - Mediapipe 辨識手指,做出手指擦除霧氣的效果

偵測滑鼠事件,用滑鼠擦除模糊

了解模糊和清楚合併的原理後,參考「在影片中即時繪圖」文章,將滑鼠繪圖的程式加入到程式裡,執行後就可以在模糊的影像中,用滑鼠擦出清楚的影像。

import cv2
import numpy as np

w = 640    # 定義影片寬度
h = 360    # 定義影像高度
dots = []  # 記錄座標
mask_b = np.zeros((h,w,3), dtype='uint8')   # 產生黑色遮罩 -> 套用清楚影像
mask_w = np.zeros((h,w,3), dtype='uint8')   # 產生白色遮罩 -> 套用模糊影像
mask_w[0:h, 0:w] = 255                      # 白色遮罩背景為白色

# 滑鼠繪圖函式
def show_xy(event,x,y,flags,param):
    global dots, mask
    if flags == 1:
        if event == 1:
            dots.append([x,y])
        if event == 4:
            dots = []
        if event == 0 or event == 4:
            dots.append([x,y])
            x1 = dots[len(dots)-2][0]
            y1 = dots[len(dots)-2][1]
            x2 = dots[len(dots)-1][0]
            y2 = dots[len(dots)-1][1]
            cv2.line(mask_w, (x1,y1), (x2,y2), (0,0,0), 50)        # 在白色遮罩上畫出黑色線條
            cv2.line(mask_b, (x1,y1), (x2,y2), (255,255,255), 50)  # 在黑色遮罩上畫出白色線條

cv2.imshow('oxxostudio', mask)                 # 啟用視窗
cv2.setMouseCallback('oxxostudio', show_xy)    # 偵測滑鼠行為

cap = cv2.VideoCapture(0)

if not cap.isOpened():
    print("Cannot open camera")
    exit()
while True:
    ret, img = cap.read()
    if not ret:
        print("Cannot receive frame")
        break

    img = cv2.resize(img,(w,h))                      # 縮小尺寸,加快速度
    img = cv2.flip(img, 1)                           # 翻轉影像
    img = cv2.cvtColor(img, cv2.COLOR_BGR2BGRA)      # 轉換顏色為 BGRA ( 計算時需要用到 Alpha 色版 )
    img2 = img.copy()                                # 複製影像
    img2 = cv2.blur(img, (55, 55))                   # 套用模糊

    mask1 = cv2.cvtColor(mask_b, cv2.COLOR_BGR2GRAY) # 轉換遮罩為灰階
    img = cv2.bitwise_and(img, img, mask=mask1)      # 清楚影像套用黑遮罩
    mask2 = cv2.cvtColor(mask_w, cv2.COLOR_BGR2GRAY) # 轉換遮罩為灰階
    img2 = cv2.bitwise_and(img2, img2, mask=mask2)   # 模糊影像套用白遮罩

    output = cv2.add(img, img2)                      # 合併影像

    cv2.imshow('oxxostudio', output)
    if cv2.waitKey(50) == ord('q'):
        break

cap.release()
cv2.destroyAllWindows()

Python 教學 - Mediapipe 辨識手指,做出手指擦除霧氣的效果

偵測手指,用手指擦除模糊

參考「Mediapipe 手掌偵測」文章,偵測手指的座標,就能透過手指擦除模糊的區域,為了區隔「什麼時候要擦」,可以計算「食指和中指的距離」,如果距離比較大就不擦 ( 手勢 YA ),距離比較小就擦 ( 兩隻手指併攏 )。

import cv2
import mediapipe as mp
import numpy as np
import math

mp_drawing = mp.solutions.drawing_utils
mp_drawing_styles = mp.solutions.drawing_styles
mp_hands = mp.solutions.hands

cap = cv2.VideoCapture(0)            # 讀取攝影機

# mediapipe 啟用偵測手掌
with mp_hands.Hands(
    model_complexity=0,
    min_detection_confidence=0.5,
    min_tracking_confidence=0.5) as hands:

    if not cap.isOpened():
        print("Cannot open camera")
        exit()

    w = 640    # 定義影片寬度
    h = 360    # 定義影像高度
    dots = []  # 記錄座標
    mask_b = np.zeros((h,w,3), dtype='uint8')            # 產生黑色遮罩 -> 套用清楚影像
    mask_w = np.zeros((h,w,3), dtype='uint8')            # 產生白色遮罩 -> 套用模糊影像
    mask_w[0:h, 0:w] = 255                               # 白色遮罩背景為白色

    while True:
        ret, img = cap.read()
        img = cv2.resize(img, (w,h))                     # 縮小尺寸,加快處理效率
        img = cv2.flip(img, 1)                           # 翻轉影像
        img_hand = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)  # 偵測手勢使用
        img = cv2.cvtColor(img, cv2.COLOR_BGR2BGRA)      # 轉換顏色為 BGRA ( 計算時需要用到 Alpha 色版 )
        img2 = img.copy()                                # 複製影像
        img2 = cv2.blur(img, (55, 55))                   # 套用模糊

        if not ret:
            print("Cannot receive frame")
            break
        results = hands.process(img_hand)                # 偵測手勢
        if results.multi_hand_landmarks:
            for hand_landmarks in results.multi_hand_landmarks:
                finger_points = []                       # 記錄手指節點位置的串列
                for i in hand_landmarks.landmark:
                    x = i.x
                    y = i.y
                    finger_points.append((x,y))          # 記錄手指節點位置
                if finger_points:
                    fx1 = finger_points[8][0]
                    fy1 = finger_points[8][1]
                    fx2 = finger_points[12][0]
                    fy2 = finger_points[12][1]
                    d = ((fx1-fx2)*(fx1-fx2)+(fy1-fy2)*(fy1-fy2))**0.5  # 計算食指和中指分開的距離
                    if d<0.15:
                        dots.append([fx1,fy1])
                        dl = len(dots)
                        if dl>1:
                            x1 = int(dots[dl-2][0]*w)   # 計算出真正的座標
                            y1 = int(dots[dl-2][1]*h)
                            x2 = int(dots[dl-1][0]*w)
                            y2 = int(dots[dl-1][1]*h)
                            cv2.line(mask_w, (x1,y1), (x2,y2), (0,0,0), 50)        # 在白色遮罩上畫出黑色線條
                            cv2.line(mask_b, (x1,y1), (x2,y2), (255,255,255), 50)  # 在黑色遮罩上畫出白色線條
                    else:
                        dots = []

        mask1 = cv2.cvtColor(mask_b, cv2.COLOR_BGR2GRAY) # 轉換遮罩為灰階
        img = cv2.bitwise_and(img, img, mask=mask1)      # 清楚影像套用黑遮罩
        mask2 = cv2.cvtColor(mask_w, cv2.COLOR_BGR2GRAY) # 轉換遮罩為灰階
        img2 = cv2.bitwise_and(img2, img2, mask=mask2)   # 模糊影像套用白遮罩

        output = cv2.add(img, img2)                      # 合併影像

        cv2.imshow('oxxostudio', output)
        keyboard = cv2.waitKey(5)
        if keyboard == ord('q'):
            break
cap.release()
cv2.destroyAllWindows()

Python 教學 - Mediapipe 辨識手指,做出手指擦除霧氣的效果

更多 Python 教學

大家好,我是 OXXO,是個即將邁入中年的斜槓青年,我已經寫了超過 400 篇 Python 的教學,有興趣可以參考下方連結呦~ ^_^


上一篇
( Day 24 ) Mediapipe 辨識手指,用手指在影片中畫圖
下一篇
( Day 26 ) Mediapipe 即時合成搞笑橘子臉
系列文
Python x AI 影像辨識好好玩32
圖片
  直播研討會

尚未有邦友留言

立即登入留言